#ifndef MANDELBROT
#define MANDELBROT

/* enable visual styles */
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

#define TIME_STRING_LEN 64
#define RESOLUTIONS 5
#define DEFAULT_RESOLUTION 0
#define WM_RENDER_DONE (WM_USER + 0)

/* ThreadStatus is just an enumeration of different states that a thread
   can be in - with IDLE being the initial state, or the state after a 
   thread has finished rendering / been successfully cancelled, CANCELLED when 
   the main thread is cancelling the render, and RENDERING when a thread is 
   rendering */
enum ThreadStatus { IDLE = 0, CANCELLED, RENDERING };

typedef struct Complex
{
	double real, imag;
} COMPLEX;

typedef struct DialogParam
{
	int iCurrResolutionOption, iCurrThreadOption;
} DIALOGPARAM;

typedef struct RenderRange
{
	double realMax, realMin, imagMax, imagMin;
} RENDERRANGE;

typedef struct ThreadParam
{
	int xImageRes, yImageRes, threadStatus, threadID;
	RECT plotArea;
	HDC hdcRender;
	HWND hWindow;
	HANDLE hSignal, hThreadKilled;
	BOOL bKill;
	RENDERRANGE range;
} THREADPARAM;

typedef struct RenderDetails
{
	int xImageRes, yImageRes, xScreenRes, yScreenRes;
	UINT iThreads, iFinishedThreads, iCores;
	LARGE_INTEGER frequency, initialTime, endTime;
	double dElapsedTime;
	BOOL bRendering;
	HBITMAP *hRenderRegions;
	RENDERRANGE range;
} RENDERDETAILS;

typedef struct MandelbrotData
{
	int xClientWidth, yClientHeight, iMaxThreads;
	BOOL bDisplayRender, bZoom;
	HBITMAP hScreenBuffer, hRender;
	TCHAR szTitleName[MAX_PATH], szFileName[MAX_PATH], szTime[TIME_STRING_LEN];
	POINT selectionStart;
	RECT zoomSelection;
	RENDERRANGE currRange;
	HINSTANCE hInstance;
	HANDLE *hThreadKillEvent;
	OPENFILENAME ofn;
} MANDELBROTDATA;

LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK PropertiesProcedure(HWND, UINT, WPARAM, LPARAM);
void ThreadProcedure(PVOID);

BOOL InitialiseMandelbrot(MANDELBROTDATA *const, RENDERDETAILS *const, 
                          DIALOGPARAM *const, HWND);
void LeftButtonUp(MANDELBROTDATA *const, RENDERDETAILS *const);
void LeftButtonDown(MANDELBROTDATA *const, RENDERDETAILS *const, const LPARAM,
                    HWND);
void RightButtonUp(MANDELBROTDATA *const, HWND);
void MouseMove(MANDELBROTDATA *const, RENDERDETAILS *const, WPARAM, 
               LPARAM, HWND);
void SaveFile(MANDELBROTDATA *const, RENDERDETAILS *const, HWND);
void Paint(MANDELBROTDATA *const, RENDERDETAILS *const, HWND);
void Copy(MANDELBROTDATA *const, HWND);
void ResetProperties(MANDELBROTDATA *const, DIALOGPARAM *const);
void FinishMandelbrot(MANDELBROTDATA *const, RENDERDETAILS *const, 
                      THREADPARAM *const, HWND);

void StartRender(MANDELBROTDATA *const, RENDERDETAILS *const, 
                 THREADPARAM *const, DIALOGPARAM *const, HWND);
void CancelRender(RENDERDETAILS *const, THREADPARAM *const, HWND);
void FinishRender(MANDELBROTDATA *const, RENDERDETAILS *const, 
                  THREADPARAM *const, HWND);

THREADPARAM *InitialiseThreads(MANDELBROTDATA *const, HWND);
void ProcessThreadOutput(MANDELBROTDATA *const, RENDERDETAILS *const, 
                         THREADPARAM *const, const int, HWND);
void KillThreads(const UINT, THREADPARAM *const);

void GetThreadCount(RENDERDETAILS *const, DIALOGPARAM *const);
void GetResolution(RENDERDETAILS *const, DIALOGPARAM *const);

void UpdateZoomSelection(const LPARAM, const POINT, RECT *const,
                         const int, const int);
void CalculateRange(RENDERDETAILS *const, RENDERRANGE *const, RECT *const);
void DefaultRange(MANDELBROTDATA *const);

void InitialiseOfnStruct(OPENFILENAME *, TCHAR *, TCHAR *, TCHAR *, HWND);

#endif